package sample.plugin.rainbow.brackets;

import com.rapid.api.component.widget.editor.HighlightToken;
import com.rapid.api.lang.engine.AbstractProvider;
import com.rapid.api.lang.engine.CancellationToken;
import com.rapid.api.lang.engine.LanguageContext;
import com.rapid.api.lang.engine.highlight.DocumentHighlight;
import com.rapid.api.lang.engine.highlight.HighlightProvider;
import com.rapid.api.lang.engine.highlight.token.IdentifierToken;
import com.rapid.api.lang.engine.highlight.token.IdentifierTokenizer;
import com.rapid.framework.data.document.Position;
import com.rapid.framework.data.document.Range;
import com.rapid.framework.vfs.VirtualFile;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class RainbowHighlightProvider extends AbstractProvider<LanguageContext> implements HighlightProvider {
    private HighlightProvider originProvider;

    public RainbowHighlightProvider(LanguageContext context, HighlightProvider originProvider) {
        super(context);
        this.originProvider = originProvider;
    }

    @Override
    public List<DocumentHighlight> documentHighlights(VirtualFile session, CancellationToken cancellationToken) {
        List<DocumentHighlight> highlights = originProvider.documentHighlights(session, cancellationToken);
        List<DocumentHighlight> bracketsHighlights = analyzeBrackets(context.getDynamicContent(session));
        highlights.addAll(bracketsHighlights);
        return highlights;
    }

    private List<DocumentHighlight> analyzeBrackets(CharSequence content) {
        List<DocumentHighlight> highlights = new ArrayList<>();
        int parenSize = 0;
        int brackSize = 0;
        int braceSize = 0;
        IdentifierTokenizer tokenizer = new IdentifierTokenizer(content);
        int token;
        int line;
        int column;
        int index;
        try {
            while ((token = tokenizer.yylex()) != IdentifierToken.EOF) {
                line = tokenizer.yyline();
                column = tokenizer.yycolumn();
                index = tokenizer.yychar();
                Position pos = posOf(line, column, index);
                switch (token) {
                    case IdentifierToken.LPAREN:
                        highlight(highlights, parenSize++, pos);
                        break;
                    case IdentifierToken.LBRACK:
                        highlight(highlights, brackSize++, pos);
                        break;
                    case IdentifierToken.LBRACE:
                        highlight(highlights, braceSize++, pos);
                        break;
                    case IdentifierToken.RPAREN:
                        if (parenSize > 0) {
                            highlight(highlights, --parenSize, pos);
                        }
                        break;
                    case IdentifierToken.RBRACK:
                        if (brackSize > 0) {
                            highlight(highlights, --brackSize, pos);
                        }
                        break;
                    case IdentifierToken.RBRACE:
                        if (braceSize > 0) {
                            highlight(highlights, --braceSize, pos);
                        }
                        break;
                }
            }
        } catch (IOException ignored) {
        }
        return highlights;
    }

    private static void highlight(
            List<DocumentHighlight> highlights, int depth, Position position) {
        DocumentHighlight highlight = new DocumentHighlight();
        highlight.color = getColor(depth);
        highlight.range = new Range(position, new Position(
                position.line, position.character + 1
        ));
        highlights.add(highlight);
    }

    private static Position posOf(int line, int column, int index) {
        return new Position(line, column, index);
    }

    private static int getColor(int stackDepth) {
        return COLOR_LEVELS[stackDepth % COLOR_LEVELS.length];
    }

    private final static int[] COLOR_LEVELS = {
            HighlightToken.CLASS,
            HighlightToken.FUNCTION,
            HighlightToken.VARIABLE,
            HighlightToken.STRING,
            HighlightToken.LITERAL,
            HighlightToken.COMMENT,
            HighlightToken.OPERATOR,
    };
}
